package main

import "fmt"
import "strings"
import "jvmgo/ch05/classfile"
import "jvmgo/ch05/classpath"

/*
public static void main(){
        int a = 5;
        int b = 6;
        int result = a + b;

    }
这段代码的字节码：
0: iconst_5			//	往栈中压入int类型的值5
1: istore_0			//	从栈中弹出一个值，并存放到局部变量表（localVars）的索引为0的位置
2: bipush 6			//	此指令有操作数，其它的指令都没有操作数，有些操作数包含在了指令中，
					//		此指令会读取一个字节的数（byte），提升为int类型，并压入栈中

4: istore_1			//	弹栈，存入localVars索引为1的位置
5: iload_0			//	从LocalVars的索引0处读取一个值，并压栈
6: iload_1			//	从LocalVars的索引1处读取一个值，并压栈
7: iadd				//	执行求和指令：没有操作数，从栈中弹出2个int类型的值，求和，并把结果压栈
8: istore_2  		//	弹栈，存入localVars索引为2的位置
9: _return			//	从方法中返回。

整个执行流程：
----bytecode len:-----10
-----pc: 0 inst:*constants.ICONST_5 &{{}}
-----pc: 1 inst:*stores.ISTORE_0 &{{}}
-----pc: 2 inst:*constants.BIPUSH &{6}
-----pc: 4 inst:*stores.ISTORE_1 &{{}}
-----pc: 5 inst:*loads.ILOAD_0 &{{}}
-----pc: 6 inst:*loads.ILOAD_1 &{{}}
-----pc: 7 inst:*math.IADD &{{}}
-----pc: 8 inst:*stores.ISTORE_2 &{{}}

LocalVars:[{5 <nil>} {6 <nil>} {11 <nil>}]
//这里输出好像栈中有数据，实际是没有的，因为add指令执行后，栈中是没有数据的，这里只是toString输出（操作数栈的实现没有真正删除）
OperandStack:&{0 [{11 <nil>} {6 <nil>}]}

*/

func main() {
	cmd := parseCmd()

	if cmd.versionFlag {
		fmt.Println("version 0.0.1")
	} else if cmd.helpFlag || cmd.class == "" {
		printUsage()
	} else {
		startJVM(cmd)
	}
}

func startJVM(cmd *Cmd) {
	fmt.Println("------------start jvm---------")
	cp := classpath.Parse(cmd.XjreOption, cmd.cpOption)
	className := strings.Replace(cmd.class, ".", "/", -1)
	cf := loadClass(className, cp)
	mainMethod := getMainMethod(cf)
	if mainMethod != nil {
		interpret(mainMethod)
	} else {
		fmt.Printf("Main method not found in class %s\n", cmd.class)
	}
}

func loadClass(className string, cp *classpath.Classpath) *classfile.ClassFile {
	classData, _, err := cp.ReadClass(className)
	if err != nil {
		panic(err)
	}

	cf, err := classfile.Parse(classData)
	if err != nil {
		panic(err)
	}

	return cf
}

func getMainMethod(cf *classfile.ClassFile) *classfile.MemberInfo {
	/*for _, m := range cf.Methods() {
		if m.Name() == "main" && m.Descriptor() == "([Ljava/lang/String;)V" {
			return m
		}
	}
	return nil*/

	for _, m := range cf.Methods() {
		if m.Name() == "main" { //AddNoFunctionCall.class文件的main方法没有String[]参数
			return m
		}
	}
	return nil
}
